/*************************************************************************
	> File Name: pthread_deadlock.c
	> Author: Angel Criss
	> Mail: abbytsing@gmail.com 
	> Created Time: Mon Jul 20 16:29:13 2014
 ************************************************************************/

/*
 * 这个程序看不到输出，因为在两个线程一开始就陷入了
 * 死锁，被阻塞了
 * 实现:在第一个loop中给lock_1上锁，在第二个loop中给lock_2上锁
 * 线程1（也可能是线程2， CPU调度决定）执行时执行没上锁的loop1完
 * 成后没有对lock1解锁又执行loop2再给lock2上锁，又没有给lock2解
 * 锁，导致其他线程被锁，自己也无缘2次循环
 */

#include<stdio.h>
#include<pthread.h>
#include<stdlib.h>
#include<unistd.h>

static int global=0;
static int global_2=0;
static pthread_mutex_t lock_1 = PTHREAD_MUTEX_INITIALIZER; // declare and initialize a mutual exclusion
static pthread_mutex_t lock_2 = PTHREAD_MUTEX_INITIALIZER; 

void err(int s, char *fun)
{
    perror(fun);
    exit(s);
}

void *func(void *arg)
{
    int loop=*((int *)arg);
    int local,i,status;

    for(i=0;i<loop;i++)
    {
        if((status=pthread_mutex_lock(&lock_1))!=0) // lock 1 
            err(status,"mutex_lock_1");

        local=global;
        local++;
        global=local;

        if((status=pthread_mutex_unlock(&lock_2))!=0) // unlock 2
            err(status,"mutex_unlock_2");
    }

    for(i=0;i<loop;i++)
    {
        if((status=pthread_mutex_lock(&lock_2))!=0) // lock 2 
            err(status,"mutex_lock_2");
        local=global_2;
        local++;
        global_2=local;

        if((status=pthread_mutex_unlock(&lock_1))!=0) // unlock 1
            err(status,"mutex_unlock_1");
    }
    return NULL;
}

int main(int argc, char *argv[])
{
    pthread_t pd1,pd2;
    void *res; // 未初始化 即: NULL
    int arg,s;

    arg=(argc>1)?atoi(argv[1]):1000;

    if((s=pthread_create(&pd1,NULL,func,&arg))!=0)
        err(s,"pthread_create1");
    if((s=pthread_create(&pd2,NULL,func,"pd2"))!=0)
        err(s,"pthread_create2");

    if((s=pthread_join(pd1,res))!=0)
        err(s,"pthread_join1");
    if((s=pthread_join(pd2,res))!=0)
        err(s,"pthread_join2");

    printf("the global is: %d\n",global);

    exit(EXIT_SUCCESS);
}
